/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

/**
 * Create a new Widget.
 *
 * @class
 * @param url {string} The URL of this Widget
 * @param state {object} The Component state: must include 'id'.
 */
BHF.Widget = function( url, state )
{
    BHF.Widget.superclass.constructor.call( this, url, state );

    /**
     * Widget state include visual state (regions and messages) beyond component state
     */
    this.updateState = function( o )
    {
        try
        {
            var state = o.state;
            var regionID;
            var that = this;
            var regionType;

            // Update state (from Component)
            if( state && state.version != this.state.version )
            {
                for( var k in state ) if( BHF.lang.hasOwnProperty( state, k ) )
                {
                    if( !BHF.lang.isFunction( state[k] ) )
                        this.state[k] = state[k];
                }
            }

            // Widget regions and scripts
            if( BHF.lang.isDefined( o.regions ) )
            {
                for( regionID in o.regions ) if( BHF.lang.hasOwnProperty( o.regions, regionID ) )
                {
                    regionType = regionID.split( ':' )[1];

                    switch( regionType )
                    {
                        case 'script':
                            eval( o.regions[ regionID ] );
                            break;

                        default:
                            var el = document.getElementById( regionID );
                            if( BHF.lang.isDefined( el ) && el !== null )
                                el.innerHTML = o.regions[ regionID ];
                            break;
                    }
                }
            }
        }
        catch( e )
        {
            alert( "Internal Widget Failure: " + e );
        }

        // Messages
    };
};
BHF.lang.extend( BHF.Widget, BHF.Component );
/*
 * Handle extracting messages from an exception and stuffing them into error divs. For each messsage of type <em>type</em> and
 * topic <em>topic</em>, it tries, in the following order, to find an associated element by ID.
 * <ol>
 *      <li><em>type</em>:<em>topic</em></li>
 *      <li><em>type</em></li>
 *      <li>'errors'</li>
 * </ol>
 * If an element is found, the <code>innerHTML</code> property is set to the summary text of the message.
 */
BHF.MessageStuffer = function(){
    var that              = {};
    var populatedElements = [];

    var isVisible = function( e )
    {
        if( BHF.lang.isDefined( YAHOO.util.Dom.getStyle ) )
        {
            return YAHOO.util.Dom.getStyle( e, 'visibility' ) !== 'hidden';
        }
        else
        {
            return true;
        }
    };

    var isVisibleGlobal = function( e )
    {
        return BHF.lang.isDefined( YAHOO.util.Dom.getStyle ) &&
            YAHOO.util.Dom.getStyle( e, 'visibility' ) !== 'hidden' &&
            (BHF.lang.isUndefined( e.id ) || BHF.lang.isNull( e.id ) || e.id.length === 0);
    };

    that.clearMessageElement = function( element )
    {
        element.innerHTML = "";
    };

    that.clearPreviousMessages = function()
    {
        var i,l;
        for( i=0,l=populatedElements.length; i < l; i++ )
            that.clearMessageElement( populatedElements[i] );
        populatedElements = [];
    };

    that.stuffMessageIntoElement = function( element, message )
    {
        element.innerHTML=message.summary; // TODO injection protection
        populatedElements.push( element );
    };

    that.tryAndStuff = function( message, id )
    {
        if( BHF.lang.isDefined( id ) )
        {
            var d = BHF.lang.isString( id ) ? document.getElementById( id ) : id;
            if( !BHF.lang.isNull( d ) && isVisible( d ) )
            {
                that.stuffMessageIntoElement( d, message );
                return true;
            }
        }
        return false;
    };

    that.stuffOne = function( message, qualifier )
    {
        var topic = BHF.lang.isDefined( qualifier ) ? qualifier + message.topic : message.topic;

        if( BHF.lang.isDefined( message.type ) && BHF.lang.isDefined( topic ) && that.tryAndStuff( message, message.type + ':' + topic ) )
            return true;

        message.summary = '[' + topic + '] ' + message.summary;

        return that.tryAndStuff( message, message.type ) || that.tryAndStuff( message, 'errors' );
    };

    that.populate = function( messages, qualifier )
    {
        var i;
        var good=true;

        if( BHF.lang.isArray( messages ) )
        {
            for( i = 0; i < messages.length; i++ )
                if( !that.stuffOne( messages[i], qualifier ) ) good=false;
            return good;
        }
        else
        {
            return this.stuffOne( messages, qualifier );
        }
    };

    that.fieldQualifiedException = function( exception, qualifier, defaultMessage )
    {
        that.fieldException( exception, defaultMessage, qualifier );
    };

    that.fieldException = function( exception, defaultMessage, qualifier )
    {
        var errorsDiv;
        var plainMessage;

        if( BHF.lang.isDefined( exception.messages ) )
        {
            if( !that.populate( exception.messages, qualifier ) )
            {
                if( !BHF.lang.isDefined( defaultMessage ) )
                {
                    if( exception.messages.length > 0 )
                        defaultMessage = exception.messages[0].summary;
                }
                alert( defaultMessage );
            }
            return;
        }

        plainMessage = exception.message || exception.name || defaultMessage || "Application Error";


        var globals = YAHOO.util.Dom.getElementsByClassName( 'errors' );
        var stuffed = false;
        var i;

        for( i = 0; i < globals.length; i++ )
        {
            if( isVisibleGlobal( globals[i] ) )
            {
                if( that.tryAndStuff( { summary: plainMessage }, globals[i] ) )
                    stuffed = true;
            }
        }

        if( !stuffed )
        {
            alert( plainMessage );
        }
    };

    return that;
};

/**
 * Workaround for FF caret bug.
 *
 * @param elements An element, or array of elements, containing text fields.
 * @param dialog The fialog containing the problem inputs
 * @param focus The element to receive focus when its all said and done
 */
BHF.fix167801 = function( elements, dialog, focus )
{
    if( YAHOO.env.ua.gecko )
    {
        if( !BHF.lang.isArray( elements ) )
            elements = [ elements ];

        /*
         * Other part of workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=167801 !!?
         * I have no explanation for why the new thread (setTimeout) is needed, but it is.
         */
        for( var i = 0; i < elements.length; i++ )
        {
            YAHOO.util.Dom.setStyle( elements[i], "overflow", "auto" );
        }

        dialog.showEvent.subscribe( function() {
            var oldStyles = [];
            for( var i = 0; i < elements.length; i++ )
            {
                oldStyles.push( YAHOO.util.Dom.getStyle( elements[i], "display" ) );
                YAHOO.util.Dom.setStyle( elements[i], "display", "none" );
            }

            var fixDisplay = function()
            {
                for( var i = 0; i < elements.length; i++ )
                {
                    YAHOO.util.Dom.setStyle( elements[i], "display", oldStyles[i] );
                }

                if( BHF.lang.isDefined( focus ) )
                {
                    try
                    {
                        YAHOO.util.Dom.get( focus ).focus();
                    }
                    catch( e ) {}
                }
            }
            setTimeout( fixDisplay, 0 );
        });
    }
}